using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;

using DataLoad.iContact.WinClient.Properties;

using Google.GData.Contacts;
using Google.GData.Extensions;
using Google.GData.Client;

using System.Net;
using System.Reflection;
using System.IO;
using System.Drawing.Imaging;

namespace DataLoad.iContact.WinClient
{
    public partial class frmMain : Form
    {
        public const int WM_NCLBUTTONDOWN = 0xA1;
        public const int HT_CAPTION = 0x2;

        #if !MONO 
        [DllImportAttribute("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
        [DllImportAttribute("user32.dll")]
        public static extern bool ReleaseCapture();
        #else
            public static int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam)
            {
                return 0;
            }

            public static bool ReleaseCapture()
            {
                return false;
            }
        #endif

        public frmMain()
        {
            InitializeComponent();

            if (Settings.Default.StartWithWindows)
            {
                Minimise();
            }
        }

        #region Snap to Screen

        private const int mSnapOffset = 35;
        private const int WM_WINDOWPOSCHANGING = 70;

        [StructLayout(LayoutKind.Sequential)]
        public struct WINDOWPOS
        {
            public IntPtr hwnd;
            public IntPtr hwndInsertAfter;
            public int x;
            public int y;
            public int cx;
            public int cy;
            public int flags;
        }

        [DebuggerStepThrough()]
        protected override void WndProc(ref Message m)
        {
            // Listen for operating system messages
            switch (m.Msg)
            {
                case WM_WINDOWPOSCHANGING:
                    {
                        SnapToDesktopBorder(this, m.LParam, 0);
                    }
                    break;
            }

            base.WndProc(ref m);
        }

        public static void SnapToDesktopBorder(Form clientForm, IntPtr LParam, int widthAdjustment)
        {
            if (clientForm == null)
            {
                // Satisfies rule: Validate parameters
                throw new ArgumentNullException("clientForm");
            }

            // Snap client to the top, left, bottom or right desktop border
            // as the form is moved near that border.

            try
            {
                // Marshal the LPARAM value which is a WINDOWPOS struct
                WINDOWPOS NewPosition = new WINDOWPOS();
                NewPosition = (WINDOWPOS)System.Runtime.InteropServices.Marshal.PtrToStructure(LParam, typeof(WINDOWPOS));

                if (NewPosition.y == 0 || NewPosition.x == 0)
                {
                    return;
                    // Nothing to do!
                }

                // Adjust the client size for borders and caption bar
                Rectangle ClientRect = clientForm.RectangleToScreen(clientForm.ClientRectangle);
                ClientRect.Width += SystemInformation.FrameBorderSize.Width - widthAdjustment;
                ClientRect.Height += (SystemInformation.FrameBorderSize.Height + SystemInformation.CaptionHeight);

                // Now get the screen working area (without taskbar)
                Rectangle WorkingRect = Screen.FromControl(clientForm).WorkingArea;

                // Left border
                if (NewPosition.x >= WorkingRect.X - mSnapOffset && NewPosition.x <= WorkingRect.X + mSnapOffset)
                {
                    NewPosition.x = WorkingRect.X;
                }

                // Get screen bounds and taskbar height
                // (when taskbar is horizontal)
                Rectangle ScreenRect = Screen.FromControl(clientForm).Bounds;
                int TaskbarHeight = ScreenRect.Height - WorkingRect.Height;

                // Top border (check if taskbar is on top
                // or bottom via WorkingRect.Y)
                if (NewPosition.y >= -mSnapOffset && (WorkingRect.Y > 0 && NewPosition.y <= (TaskbarHeight + mSnapOffset)) || (WorkingRect.Y <= 0 && NewPosition.y <= (mSnapOffset)))
                {
                    if (TaskbarHeight > 0)
                    {
                        NewPosition.y = WorkingRect.Y;
                        // Horizontal Taskbar
                    }
                    else
                    {
                        NewPosition.y = 0;
                        // Vertical Taskbar
                    }
                }

                // Right border
                if (NewPosition.x + ClientRect.Width <= WorkingRect.Right + mSnapOffset && NewPosition.x + ClientRect.Width >= WorkingRect.Right - mSnapOffset)
                {
                    NewPosition.x = WorkingRect.Right - (ClientRect.Width + SystemInformation.FrameBorderSize.Width);
                }

                // Bottom border
                if (NewPosition.y + ClientRect.Height <= WorkingRect.Bottom + mSnapOffset && NewPosition.y + ClientRect.Height >= WorkingRect.Bottom - mSnapOffset)
                {
                    NewPosition.y = WorkingRect.Bottom - (ClientRect.Height + SystemInformation.FrameBorderSize.Height);
                }

                // Marshal it back
                System.Runtime.InteropServices.Marshal.StructureToPtr(NewPosition, LParam, true);
            }
            catch
            {
            }
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                ReleaseCapture();
                SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
            }
        }

        #endregion

        private const int PAGE_SIZE = 50;

        private ContactsService _service;

        private bool _loadedAll = false;

        private bool _runMinimised = false;

        private bool _connectionIssue = false;

        private ContactCollection _allcontacts;

        private Encryptor _encryptor = new Encryptor(Encryptor.SymmProvEnum.DES);

        public ContactCollection AllContacts
        {
            get
            {
                return _allcontacts;
            }
        }

        private ContactCollection _displayedContacts;

        private Label _selectedLabel;

        private bool _forceClose = false;

        /// <summary>
        /// <remarks>Never set directly. Use EnterEditMode and ExitEditMode methods</remarks>
        /// </summary>
        private bool _inEditMode = false;

        private MinimiseOnCloseOptions _minimiseOnClose = MinimiseOnCloseOptions.No;

        private NameDisplayType _nameOrder = NameDisplayType.NoChange;

        internal DialogResult ShowLogin(string captchaToken)
        {
            if (frmLogin.Show(captchaToken) == DialogResult.OK)
            {
                _service.setUserCredentials(LoginManager.Username, LoginManager.Password);

                if (LoginManager.CaptchaRequired)
                {
                    GDataGAuthRequestFactory reqFactory = _service.RequestFactory as GDataGAuthRequestFactory;
                    reqFactory.CaptchaAnswer = LoginManager.CaptchaAnswer;
                    reqFactory.CaptchaToken = LoginManager.CaptchaToken;
                }

                return DialogResult.OK;
            }
            else
            {
                _connectionIssue = true;

                return DialogResult.Cancel;
            }
        }

        public void EnterEditMode()
        {
            if (!_inEditMode)
            {
                DLContact contact = lstContacts.SelectedItem as DLContact;

                btnCancel.Show();
                btnEdit.Text = "Save";

                _inEditMode = true;

                //lstContacts.Enabled = false;

                DisplayContact(contact);
            }
        }

        public void ExitEditMode(bool save)
        {
            if (_inEditMode)
            {
                btnCancel.Hide();
                btnEdit.Text = "Edit";

                _inEditMode = false;

                DLContact contact = lstContacts.SelectedItem as DLContact;

                if (contact != null)
                {
                    DisplayContact(contact);

                    if (save)
                    {
                        contact.Save();
                    }
                    else
                    {
                        // Get it again from the server
                        contact.Load();
                        DisplayContact(contact);
                        //LoadAllContacts(true);
                    }
                }

                lstContacts.Enabled = true;
            }
        }


        internal DialogResult ShowLogin()
        {
            return ShowLogin(string.Empty);
        }

        private System.Windows.Forms.Timer _searchDelayTimer;

        private System.Windows.Forms.Timer _listBoxDelayTimer;

        private void frmMain_Load(object sender, EventArgs e)
        {
            frmCheckForUpdates.CheckforUpdates();

            this.Width = Settings.Default.WindowW;
            this.Height = Settings.Default.WindowH;
            this.Top = Settings.Default.WindowT;
            this.Left = Settings.Default.WindowL;
            _runMinimised = Settings.Default.RunMinimised;
            lstContacts.Width = Settings.Default.ContactsPanelSize;
            _minimiseOnClose = (MinimiseOnCloseOptions)Enum.Parse(typeof(MinimiseOnCloseOptions), Settings.Default.MinimiseOnClose);
            _nameOrder = (NameDisplayType)Settings.Default.NameOrder;

            string applicationName = "DataLoad-iContact";

            _service = new ContactsService(string.Format("{0}-{1}", applicationName, Assembly.GetExecutingAssembly().GetName().Version.ToString()));

            DLContact.Service = _service;
            DLContact.MainForm = this;

            if (Settings.Default.UseProxy)
            {
                GDataGAuthRequestFactory reqFactory = _service.RequestFactory as GDataGAuthRequestFactory;

                reqFactory.Proxy = new WebProxy(Settings.Default.ProxyURL, Settings.Default.ProxyPort);
                reqFactory.Proxy.Credentials = new NetworkCredential(Settings.Default.ProxyUsername, _encryptor.Decrypting(Settings.Default.ProxyPassword, Environment.UserName + "GoogleContacts"));
            }

            _allcontacts = new ContactCollection();
            _displayedContacts = new ContactCollection();
            LoginManager.Load();

            _searchDelayTimer = new System.Windows.Forms.Timer();
            _searchDelayTimer.Interval = Settings.Default.SearchDelay;
            _searchDelayTimer.Tick += new EventHandler(_delayTimer_Tick);

            _listBoxDelayTimer = new System.Windows.Forms.Timer();
            _listBoxDelayTimer.Interval = Settings.Default.SearchDelay;
            _listBoxDelayTimer.Tick += new EventHandler(_listBoxDelayTimer_Tick);

            if ((!LoginManager.RememberLogin) || ((Control.ModifierKeys & Keys.Control) == Keys.Control))
            {
                if (!_runMinimised)
                {
                    if (ShowLogin() == DialogResult.Cancel)
                    {
                        Close();
                    }
                }
            }
            else
            {
                _service.setUserCredentials(LoginManager.Username, LoginManager.Password);
                if (_runMinimised)
                {
                    Minimise();
                }
            }

            LoadAllContacts(true);

            edtSearch.Focus();
        }

        void _listBoxDelayTimer_Tick(object sender, EventArgs e)
        {
            _listBoxDelayTimer.Stop();

            DLContact contact = lstContacts.SelectedItem as DLContact;

            if (contact != null)
            {
                DisplayContact(contact);
            }
        }

        void _delayTimer_Tick(object sender, EventArgs e)
        {
            _searchDelayTimer.Stop();
            ShowContactList();
        }

        private void LoadContactsByPage(int page)
        {
            ThreadPool.QueueUserWorkItem(delegate(object state)
            {
                DoLoadContactsByPage(page);
            });
        }

        public void LoadAllContacts(bool forceLoad)
        {
            if (forceLoad)
            {
                _loadedAll = false;

                _allcontacts.Clear();
                _displayedContacts.Clear();
                lstContacts.DataSource = null;
            }

            ThreadPool.QueueUserWorkItem(delegate(object state)
            {
                if (InvokeRequired)
                {
                    Invoke((ThreadStart)delegate
                    {
                        picWait1.Show();
                    });
                }
                else
                {
                    picWait1.Show();
                }

                try
                {
                    int idx = 0;
                    while ((!_loadedAll) && (!_connectionIssue))
                    {
                        DoLoadContactsByPage(idx);
                        idx++;
                    }
                }
                finally
                {
                    if (InvokeRequired)
                    {
                        Invoke((ThreadStart)delegate
                        {
                            picWait1.Hide();
                            ShowSelectedContact();
                        });
                    }
                    else
                    {
                        picWait1.Hide();
                        ShowSelectedContact();
                    }
                }

                if (!string.IsNullOrEmpty(Settings.Default.LastContact))
                {
                    DLContact lastContact = _allcontacts.FindBySelfUri(Settings.Default.LastContact);

                    if (lastContact != null)
                    {
                        if (InvokeRequired)
                        {
                            Invoke((ThreadStart)delegate
                            {
                                lstContacts.SelectedItem = lastContact;
                                edtSearch.Focus();
                            });
                        }
                        else
                        {
                            lstContacts.SelectedItem = lastContact;
                            edtSearch.Focus();
                        }
                    }
                }
            });
        }

        private void ShowSelectedContact()
        {
            if (lstContacts.SelectedItem != null)
            {
                DisplayContact(lstContacts.SelectedItem as DLContact);
            }
        }

        private void DoCloseForm()
        {
            if (InvokeRequired)
            {
                Invoke((ThreadStart)delegate
                {
                    Application.Exit();
                });
            }
            else
            {
                Application.Exit();
            }
        }

        private void DoLoadContactsByPage(int page)
        {
            if (!_loadedAll)
            {
                int startIndex = (page * PAGE_SIZE) + 1;

                ContactsQuery query = new ContactsQuery(ContactsQuery.CreateContactsUri("default"));
                query.StartIndex = startIndex;
                query.NumberToRetrieve = PAGE_SIZE;

                bool successfulLogin = false;
                ContactsFeed feed = null;

                while (!successfulLogin)
                {
                    try
                    {
                        feed = _service.Query(query);
                        successfulLogin = true;
                        _connectionIssue = false;
                    }
                    catch (GDataRequestException)
                    {
                        _connectionIssue = true;
                        ShowMessage("Unable to connect to Google servers", "Connection Error", MessageBoxIcon.Error, MessageBoxButtons.OK);
                        break;
                    }
                    catch (CaptchaRequiredException exc)
                    {
                        // Captcha required
                        if (ShowLogin(exc.Token) == DialogResult.Cancel)
                        {
                            DoCloseForm();
                        }
                    }
                    catch (InvalidCredentialsException)
                    {
                        if (ShowLogin() == DialogResult.Cancel)
                        {
                            DoCloseForm();
                        }
                    }
                }

                if (!_connectionIssue)
                {
                    if (InvokeRequired)
                    {
                        Invoke((ThreadStart)delegate
                        {
                            picConntection.Image = Resources.earth_connection;
                        });

                    }
                    else
                    {
                        picConntection.Image = Resources.earth_connection;
                    }

                    if (feed.Entries.Count != 0)
                    {
                        foreach (ContactEntry entry in feed.Entries)
                        {
                            _allcontacts.Add(new DLContact(entry, _nameOrder, false));
                        }

                        if (InvokeRequired)
                        {
                            Invoke((ThreadStart)delegate
                            {
                                lstContacts.BeginUpdate();
                            });
                        }
                        else
                        {
                            lstContacts.BeginUpdate();
                        }
                        try
                        {
                            _displayedContacts.Clear();
                            _displayedContacts.AddRange(_allcontacts);
                            _displayedContacts.Sort();

                            if (!string.IsNullOrEmpty(edtSearch.Text.Trim()))
                            {
                                FindContacts(edtSearch.Text);
                            }

                            if (InvokeRequired)
                            {
                                Invoke((ThreadStart)delegate
                                {
                                    lstContacts.DataSource = _displayedContacts;
                                    lblContactCount.Text = ContactCountString();
                                });
                            }
                            else
                            {
                                lstContacts.DataSource = _displayedContacts;
                                lblContactCount.Text = ContactCountString();
                            }

                        }
                        finally
                        {
                            if (InvokeRequired)
                            {
                                Invoke((ThreadStart)delegate
                                {
                                    lstContacts.EndUpdate();
                                });
                            }
                            else
                            {
                                lstContacts.EndUpdate();
                            }
                        }
                    }
                    else
                    {
                        _loadedAll = true;
                    }
                }
                else
                {
                    if (InvokeRequired)
                    {
                        Invoke((ThreadStart)delegate
                        {
                            picConntection.Image = Resources.earth_delete;
                        });
                    }
                    else
                    {
                        picConntection.Image = Resources.earth_delete;
                    }
                }
            }
        }

        private string ContactCountString()
        {
            if (_displayedContacts.Count != _allcontacts.Count)
            {
                return string.Format("{0} found", _displayedContacts.Count);
            }
            else
            {
                return _displayedContacts.Count > 1 ? string.Format("{0} contacts", _displayedContacts.Count) :
                    _displayedContacts.Count == 1 ? "1 contact" : "No contacts";
            }
        }

        private bool FindInOrganisation(DLContact contact, string text)
        {
            foreach (Organization org in contact.Contact.Organizations)
            {
                if (((!string.IsNullOrEmpty(org.Title)) && (org.Title.ToLower().Contains(text.ToLower()))) ||
                    ((!string.IsNullOrEmpty(org.Name)) && (org.Name.ToLower().Contains(text.ToLower()))))
                {
                    return true;
                }
            }

            return false;
        }

        private bool FindInEmails(DLContact contact, string text)
        {
            text = text.ToLower();

            foreach (EMail email in contact.Contact.Emails)
            {
                if (!string.IsNullOrEmpty(email.Address))
                {
                    string address = email.Address.ToLower();

                    if (address.Contains(text))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        private void ShowContactList()
        {
            lstContacts.BeginUpdate();
            try
            {
                FindContacts(edtSearch.Text);

                _displayedContacts.Sort();

                lblContactCount.Text = ContactCountString();
                lstContacts.DataSource = _displayedContacts;

                if (lstContacts.SelectedItem != null)
                {
                    DisplayContact(lstContacts.SelectedItem as DLContact);
                }
                else
                {
                    // Don't show anything
                    HideContactInfo();
                }
            }
            finally
            {
                lstContacts.EndUpdate();
            }
        }

        private void HideContactInfo()
        {
            foreach (Control ct in scrollArea.Controls)
            {
                ct.Hide();
            }
        }

        private MatchRate _matchThreshold = new MatchRate(MatchRate.Normal);

        private bool FuzzyFind(DLContact contact, string text)
        {
            int thresholdPrefrence = _matchThreshold.Value;

            switch (Settings.Default.FuzzySearch)
            {
                case 0:
                    thresholdPrefrence = MatchRate.NoMatch;
                    break;
                case 1:
                    thresholdPrefrence = MatchRate.Strong;
                    break;
                case 2:
                    thresholdPrefrence = MatchRate.Normal;
                    break;
                case 3:
                    thresholdPrefrence = MatchRate.Minimal;
                    break;
            }

            _matchThreshold = new MatchRate(thresholdPrefrence);

            if (_matchThreshold.Value != MatchRate.NoMatch)
            {
                // Parse name
                NameParser parser = contact.Parser;

                // Use fuzzy search in both firstand last names
                MatchRate fNameRate = DoubleMetaphone.Match(parser.FirstName, text);
                MatchRate lNameRate = DoubleMetaphone.Match(parser.LastName, text);

                int maxMatch = Math.Max(fNameRate.Value, lNameRate.Value);

                switch (maxMatch)
                {
                    case MatchRate.NoMatch:
                        contact.ResultType = ContactResultType.NoMatch;
                        break;
                    case MatchRate.Minimal:
                        contact.ResultType = ContactResultType.MinimalMatch;
                        break;
                    case MatchRate.Normal:
                        contact.ResultType = ContactResultType.NormalMatch;
                        break;
                    case MatchRate.Strong:
                        contact.ResultType = ContactResultType.StrongMatch;
                        break;
                }

                if ((fNameRate.Value >= _matchThreshold.Value) ||
                    (lNameRate.Value >= _matchThreshold.Value))
                {
                    return true;
                }

                return false;
            }
            else
            {
                return false;
            }
        }

        private void FindContacts(string text)
        {
            _displayedContacts = new ContactCollection(_allcontacts.FindAll(delegate(DLContact match)
            {
                // Look into everything (title, org and notes)
                // TODO: Can we highlight the search text in contacts data?
                bool titleMatch = (match.Contact.Title.Text.ToLower().Contains(text.ToLower()));
                bool otherEntityMatch = false;
                bool fuzzyMatch = false;

                if (!titleMatch)
                {
                    otherEntityMatch = (FindInEmails(match, text)) ||
                                       (((match.Contact.Content != null) && (!string.IsNullOrEmpty(match.Contact.Content.Content))) && (match.Contact.Content.Content.ToLower().Contains(text.ToLower())) ||
                                       (FindInOrganisation(match, text)));

                    if (!otherEntityMatch)
                    {
                        fuzzyMatch = FuzzyFind(match, text);
                    }
                    else
                    {
                        match.ResultType = ContactResultType.OtherEntityMatch;
                    }
                }
                else
                {
                    match.ResultType = ContactResultType.ExactMatch;
                }

                return titleMatch || otherEntityMatch || (fuzzyMatch);
            }));
        }

        private void edtSearch_EditValueChanging(object sender, EventArgs args)
        {
            if (_searchDelayTimer.Enabled)
            {
                _searchDelayTimer.Stop();
            }

            _searchDelayTimer.Start();
        }

        #region Generic

        private string PhoneItemName(string defaultName, PhoneNumber item)
        {
            string rel = item.Rel;
            if ((!string.IsNullOrEmpty(rel)) && (rel.IndexOf('#') != -1))
            {
                rel = rel.Substring(rel.IndexOf('#') + 1);
            }
            else
            {
                rel = defaultName;
            }

            switch (rel)
            {
                //#home, #work, #other, #home_fax, #work_fax, #mobile, #pager
                case "home":
                    return "Home";
                case "work":
                    return "Work";
                case "other":
                    return "Other";
                case "home_fax":
                    return "Home Fax";
                case "work_fax":
                    return "Work Fax";
                case "mobile":
                    return "Mobile";
                case "pager":
                    return "Pager";

                default:
                    return defaultName;
            }
        }

        private string ItemName(string defaultName, CommonAttributesElement item)
        {
            string result = defaultName;

            if (item is PhoneNumber)
            {
                return PhoneItemName(defaultName, item as PhoneNumber);
            }
            else
            {
                if (item.Work)
                {
                    result = "Work";
                }
                else if (item.Home)
                {
                    result = "Home";
                }
                else if (item.Other)
                {
                    result = "Other";
                }

                return result;
            }
        }

        void lblKey_Click(object sender, EventArgs e)
        {
            Label lbl = (sender as Label);
            _selectedLabel = lbl;
            mnuLabels.Show(lbl.Parent, lbl.Location.X, lbl.Location.Y + lbl.Height);
        }

        private void ShowItemsInGroup<T>(string defaultName, ImageGroupBox group, ExtensionCollection<T> items, ref int groupTop) where T : CommonAttributesElement
        {
            ClearGroup(group, items.Count, groupTop);

            if (items.Count > 0)
            {
                group.SuspendLayout();

                try
                {
                    int top = 23;
                    group.Controls.Clear();
                    foreach (T item in items)
                    {
                        ExLabelControl lblKey = new ExLabelControl();
                        lblKey.Text = ItemName(defaultName, item);

                        lblKey.Left = 5;
                        lblKey.RoundColor = Color.DarkGray;
                        lblKey.Top = top;
                        lblKey.Click += new EventHandler(lblKey_Click);
                        //lblKey.DropDownItems = new string[] { "Work", "Home", "Other" };
                        //lblKey.DoubleClick += new EventHandler(lblKey_DoubleClick);
                        lblKey.DisplayDropDown = true;
                        lblKey.ClickMenu = mnuLabels;
                        lblKey.Tag = item;
                        lblKey.Height = 15;
                        lblKey.Width = 75;
                        lblKey.InEditMode = _inEditMode;

                        group.Controls.Add(lblKey);

                        if (item.Primary)
                        {
                            lblKey.Font = new Font(lblKey.Font, lblKey.Font.Style | FontStyle.Bold);
                        }

                        int valueLeft = lblKey.Right + 10;

                        Label lblValue = new Label();
                        lblValue.Text = item.Value;
                        lblValue.AutoSize = true;
                        lblValue.Left = valueLeft;
                        lblValue.Top = top;
                        //lblValue.DoubleClick += new EventHandler(lblValue_DoubleClick);

                        group.Controls.Add(lblValue);

                        valueLeft += lblValue.Width + 5;

                        if (_inEditMode)
                        {
                            PictureBox editImg = new PictureBox();
                            editImg.Image = Resources.server_edit;
                            editImg.SizeMode = PictureBoxSizeMode.AutoSize;
                            editImg.Top = top;
                            editImg.Left = valueLeft;
                            editImg.Show();
                            editImg.Click += new EventHandler(editImg_Click);
                            editImg.Tag = item;

                            valueLeft += 20;

                            PictureBox removeImg = new PictureBox();
                            removeImg.Image = Resources.Close_16x16;
                            removeImg.SizeMode = PictureBoxSizeMode.AutoSize;
                            removeImg.Top = top;
                            removeImg.Left = valueLeft;
                            removeImg.Show();
                            removeImg.Click += new EventHandler(removeImg_Click);
                            removeImg.Tag = item;

                            lblValue.Tag = new LabelTagCollection(item, removeImg);

                            group.Controls.Add(removeImg);
                            group.Controls.Add(editImg);
                        }

                        top += lblValue.Height + 5;
                    }

                    group.Height = top;
                }
                finally
                {
                    group.ResumeLayout();
                }
            }

            SetNewTop(group, ref groupTop);
        }

        void editImg_Click(object sender, EventArgs e)
        {
            DLContact contact = lstContacts.SelectedItem as DLContact;

            if (contact != null)
            {
                EditItem(contact, (sender as Control).Tag);

                DisplayContact(contact);
            }
        }

        private void EditNotes(DLContact contact)
        {
            using (frmNotesEdit frm = new frmNotesEdit())
            {
                if (frm.Show(contact.Notes) == DialogResult.OK)
                {
                    contact.Notes = frm.Value;
                }
            }
        }

        private string[] GetOptions(object item)
        {
            List<string> result = new List<string>();

            if (item is IMAddress)
            {
                result.AddRange(new string[] { "Google Talk", "AIM", "Yahoo!", "MSN", "ICQ", "Jabber" });
            }
            else if (item is EMail)
            {
                result.AddRange(new string[] { "Home", "Work", "Other" });
            }
            else if (item is PostalAddress)
            {
                result.AddRange(new string[] { "Home", "Work", "Other" });
            }
            else if (item is Organization)
            {
                result.Add("Organisation");
            }
            else if (item is PhoneNumber)
            {
                result.AddRange(new string[] { "Home", "Work", "Mobile", "Home Fax", "Work Fax", "Pager", "Other" });
            }

            return result.ToArray();
        }

        private int GetSelectedOption(object item)
        {
            List<string> values = new List<string>(GetOptions(item));
            
            if (item is PhoneNumber)
            {
                string phoneType = PhoneItemName("Home", item as PhoneNumber);

                return values.IndexOf(phoneType);
            }
            else if (item is IMAddress)
            {
                Image keyImage = null;
                string imType = IMName("Google Talk", item as IMAddress, out keyImage);

                return values.IndexOf(imType);
            }
            else if (item is EMail)
            {
                EMail cItem = item as EMail;

                if (cItem.Home)
                {
                    return 0;
                }
                else if (cItem.Work)
                {
                    return 1;
                }
                else if (cItem.Other)
                {
                    return 2;
                }
                else
                {
                    return 0;
                }
            }
            else if (item is CommonAttributesElement)
            {
                CommonAttributesElement cItem = item as CommonAttributesElement;

                if (cItem.Home)
                {
                    return 0;
                }
                else if (cItem.Work)
                {
                    return 1;
                }
                else if (cItem.Other)
                {
                    return 2;
                }
                else
                {
                    return 0;
                }
            }
            else
            {
                return 0;
            }
        }

        private string GetValue(object item)
        {
            if (item is EMail)
            {
                return (item as EMail).Address;
            }
            else
            {
                return (item as CommonAttributesElement).Value;
            }
        }

        private void SetSelectedOption(object item, string value)
        {
            string result = "other";

            switch (value)
            {
                case "Home":
                    result = "home";
                    break;
                case "Work":
                    result = "work";
                    break;
                case "Other":
                    result = "other";
                    break;
                case "Home Fax":
                    result = "home_fax";
                    break;
                case "Work Fax":
                    result = "work_fax";
                    break;
                case "Pager":
                    result = "pager";
                    break;
                case "Mobile":
                    result = "mobile";
                    break;
                case "Yahoo!":
                    result = "YAHOO";
                    break;
                case "MSN":
                    result = "MSN";
                    break;
                case "Jabber":
                    result = "JABBER";
                    break;
                case "AIM":
                    result = "AIM";
                    break;
                case "ICQ":
                    result = "ICQ";
                    break;
                case "Google Talk":
                    result = "GOOGLE_TALK";
                    break;
                default:
                    result = value;
                    break;
            }

            if (item is CommonAttributesElement)
            {
                (item as CommonAttributesElement).Rel = null;
                (item as CommonAttributesElement).Label = null;
                (item as CommonAttributesElement).Rel = string.Concat("http://schemas.google.com/g/2005#", result);
            }
            else if (item is IMAddress)
            {
                (item as IMAddress).Protocol = string.Concat("http://schemas.google.com/g/2005#", result);
            }
            else if (item is EMail)
            {
                (item as EMail).Rel = null;
                (item as EMail).Label = null;
                (item as EMail).Rel = string.Concat("http://schemas.google.com/g/2005#", result);
            }
        }

        private void SetValue(object item, string value)
        {
            if (item is EMail)
            {
                (item as EMail).Address = value;
            }
            else
            {
                (item as CommonAttributesElement).Value = value;
            }
        }

        private void EditItem(DLContact contact, object item)
        {
            if (item is PostalAddress)
            {
                using (frmAddressEdit frm = new frmAddressEdit())
                {
                    if (frm.Show("Edit", GetOptions(item), GetValue(item), GetSelectedOption(item)) == DialogResult.OK)
                    {
                        SetValue(item, frm.Value);
                        SetSelectedOption(item, frm.SelectedOption);
                    }
                }
            }
            else
            {
                using (EditForm frm = new EditForm())
                {
                    if (frm.Show("Edit", GetOptions(item), GetValue(item), GetSelectedOption(item)) == DialogResult.OK)
                    {
                        SetValue(item, frm.Value);
                        SetSelectedOption(item, frm.SelectedOption);
                    }
                }
            }
        }

        #endregion

        #region Email

        private void ShowEmailsInGroup<T>(string defaultName, ImageGroupBox group, ExtensionCollection<T> items, ref int groupTop) where T : EMail
        {
            ClearGroup(group, items.Count, groupTop);

            if (items.Count > 0)
            {
                group.SuspendLayout();

                try
                {
                    group.Controls.Clear();

                    int top = 23;

                    foreach (T item in items)
                    {
                        ExLabelControl lblKey = new ExLabelControl();
                        if (item is IMAddress)
                        {
                            Image img = null;
                            lblKey.Text = IMName(defaultName, item as IMAddress, out img);
                            if (img != null)
                            {
                                lblKey.Image = img;
                                lblKey.ImageAlign = ContentAlignment.MiddleLeft;
                                lblKey.DisplayDropDown = true;
                                lblKey.ClickMenu = mnuLabels;
                                lblKey.Click += new EventHandler(lblKey_Click);
                                lblKey.AutoSize = false;
                                lblKey.Width = 100;
                                lblKey.Height = 20;
                            }
                        }
                        else
                        {
                            lblKey.Text = EmailName(defaultName, item);
                            lblKey.Width = 75;
                            lblKey.Height = 15;
                        }

                        //lblKey.DoubleClick += new EventHandler(lblKey_DoubleClick);
                        //lblKey.DropDownItems = new string[] { "Work", "Home", "Other" };
                        lblKey.InEditMode = _inEditMode;
                        lblKey.Left = 5;
                        lblKey.RoundColor = Color.DarkGray;
                        lblKey.Top = top;
                        lblKey.Tag = item;

                        if (!(item is IMAddress))
                        {
                            lblKey.Click += new EventHandler(lblKey_Click);
                            lblKey.DisplayDropDown = true;
                        }

                        group.Controls.Add(lblKey);

                        if (item.Primary)
                        {
                            lblKey.Font = new Font(lblKey.Font, lblKey.Font.Style | FontStyle.Bold);
                        }

                        int valueLeft = lblKey.Right + 10;

                        Label lblValue = new Label();
                        lblValue.Text = item.Address;
                        lblValue.AutoSize = true;
                        lblValue.Left = valueLeft;
                        //lblValue.DoubleClick += new EventHandler(lblValue_DoubleClick);
                        if (item is IMAddress)
                        {
                            lblValue.Top = top + 4;
                        }
                        else
                        {
                            lblValue.Top = top;
                        }

                        group.Controls.Add(lblValue);

                        valueLeft += lblValue.Width + 5;

                        if (_inEditMode)
                        {
                            PictureBox editImg = new PictureBox();
                            editImg.Image = Resources.server_edit;
                            editImg.SizeMode = PictureBoxSizeMode.AutoSize;
                            editImg.Top = top;
                            editImg.Left = valueLeft;
                            editImg.Show();
                            editImg.Click += new EventHandler(editImg_Click);
                            editImg.Tag = item;

                            valueLeft += 20;
                            
                            PictureBox removeImg = new PictureBox();
                            removeImg.Image = Resources.Close_16x16;
                            removeImg.SizeMode = PictureBoxSizeMode.AutoSize;
                            removeImg.Top = top;
                            removeImg.Left = valueLeft;
                            removeImg.Show();
                            removeImg.Click += new EventHandler(removeImg_Click);
                            removeImg.Tag = item;


                            lblValue.Tag = new LabelTagCollection(item, removeImg);

                            group.Controls.Add(removeImg);
                            group.Controls.Add(editImg);
                        }

                        top += lblKey.Height + 5;
                    }

                    group.Height = top;
                }
                finally
                {
                    group.ResumeLayout();
                }
            }

            SetNewTop(group, ref groupTop);
        }

        void cb_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
            {
                e.SuppressKeyPress = true;
                cb_Leave(sender, e);
            }
        }

        void cb_Leave(object sender, EventArgs e)
        {
            ComboBox cb = sender as ComboBox;
            ExLabelControl label = cb.Tag as ExLabelControl;

            label.Text = cb.SelectedItem.ToString();

            if (label.Parent.Controls.Contains(cb))
            {
                label.Parent.Controls.Remove(cb);
            }

            object item = label.Tag;

            if (item is IMAddress)
            {
                IMAddress im = item as IMAddress;

                im.Rel = label.Text.ToUpper();
            }
            else if (item is EMail)
            {
                EMail mail = item as EMail;

                mail.Rel = string.Format("{0}#{1}", mail.XmlNameSpace, label.Text.ToLower());
            }

            // TODO: Add more entity types

            cb.Hide();
            label.Show();
        }

        private string IMName(string defaultName, IMAddress item, out Image keyImage)
        {
            string protocol = item.Protocol;

            keyImage = null;

            if ((!string.IsNullOrEmpty(protocol)) && (protocol.IndexOf('#') != -1))
            {
                protocol = protocol.Substring(protocol.IndexOf('#') + 1);
            }
            else
            {
                protocol = defaultName;
            }

            string result = protocol;

            switch (protocol.ToUpper())
            {
                case "YAHOO":
                    result = "Yahoo!";
                    keyImage = Resources.yahoomess;
                    break;
                case "GOOGLE_TALK":
                    result = "Google Talk";
                    keyImage = Resources.gtalk;
                    break;
                case "AIM":
                    result = "AIM";
                    keyImage = Resources.aim;
                    break;
                case "ICQ":
                    result = "ICQ";
                    keyImage = Resources.icq;
                    break;
                case "JABBER":
                    result = "Jabber";
                    keyImage = Resources.jabber;
                    break;
                case "MSN":
                    result = "MSN";
                    keyImage = Resources.wlm;
                    break;
            }

            return result;
        }

        private string EmailName(string defaultName, EMail item)
        {
            string result = defaultName;

            if (item.Work)
            {
                result = "Work";
            }
            else if (item.Home)
            {
                result = "Home";
            }
            else if (item.Other)
            {
                result = "Other";
            }

            return result;
        }

        /*void lblValue_DoubleClick(object sender, EventArgs e)
        {
            if (_inEditMode)
            {
                Label label = sender as Label;
                LabelTagCollection tags = label.Tag as LabelTagCollection;

                PictureBox removeImg = tags.RemoveImg;
                removeImg.Hide();
                // Show the editor instead
                ExTextBox editor = new ExTextBox();

                editor.Left = label.Left;

                //editor.Width = label.Parent.ClientRectangle.Width - editor.Left;
                editor.Width = 200;

                if (tags.Item is PostalAddress)
                {
                    editor.Multiline = true;
                    editor.Height = label.Height;
                }

                editor.Top = label.Top - 3;
                label.Parent.Controls.Add(editor);
                editor.Text = label.Text;
                editor.ShouldExit += new EventHandler(editor_ShouldExit);
                editor.Tag = label;
                label.Hide();
                editor.Show();
            }
        }*/

        void editor_ShouldExit(object sender, EventArgs e)
        {
            ExTextBox editor = sender as ExTextBox;
            Label label = editor.Tag as Label;

            if (!string.IsNullOrEmpty(editor.Text.Trim()))
            {
                label.Text = editor.Text;
                LabelTagCollection tags = label.Tag as LabelTagCollection;
                PictureBox removeImg = tags.RemoveImg;
                if (_inEditMode)
                {
                    removeImg.Left = label.Right + 5;
                    removeImg.Show();
                }

                // Update the value
                if (tags.Item is EMail)
                {
                    EMail email = tags.Item as EMail;
                    email.Address = editor.Text;
                }
                else if (tags.Item is IMAddress)
                {
                    IMAddress im = tags.Item as IMAddress;
                    im.Address = editor.Text;
                }
                else if (tags.Item is PostalAddress)
                {
                    PostalAddress address = tags.Item as PostalAddress;
                    address.Value = editor.Text;
                }
                else if (tags.Item is PhoneNumber)
                {
                    PhoneNumber phone = tags.Item as PhoneNumber;
                    phone.Value = editor.Text;
                }
                else if (tags.Item is Organization)
                {
                    Organization org = tags.Item as Organization;
                    org.Title = editor.Text;
                }
            }

            label.Show();
        }

        private void RemoveItem(DLContact contact, object item)
        {
            if (MessageBox.Show("Are you sure?", "Remove contact information", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                if (item is IMAddress)
                {
                    contact.Contact.IMs.Remove(item as IMAddress);
                }
                else if (item is EMail)
                {
                    contact.Contact.Emails.Remove(item as EMail);
                }
                else if (item is PostalAddress)
                {
                    contact.Contact.PostalAddresses.Remove(item as PostalAddress);
                }
                else if (item is Organization)
                {
                    contact.Contact.Organizations.Remove(item as Organization);
                }
                else if (item is PhoneNumber)
                {
                    contact.Contact.Phonenumbers.Remove(item as PhoneNumber);
                }

                contact.ContactChanged = true;
            }
        }

        private void AddItem(DLContact contact, object item)
        {
            if (item is IMAddress)
            {
                contact.Contact.IMs.Add(new IMAddress("new IM"));
            }
            else if (item is EMail)
            {
                contact.Contact.Emails.Add(new EMail("new Email"));
            }
            else if (item is PostalAddress)
            {
                contact.Contact.PostalAddresses.Add(new PostalAddress(string.Format("Line 1{0}Line 2{0}Line 3", Environment.NewLine)));
            }
            else if (item is Organization)
            {
                Organization org = new Organization();
                org.Title = "new Org";
                contact.Contact.Organizations.Add(org);
            }
            else if (item is PhoneNumber)
            {
                contact.Contact.Phonenumbers.Add(new PhoneNumber("new phone"));
            }

            contact.ContactChanged = true;
        }

        void removeImg_Click(object sender, EventArgs e)
        {
            DLContact contact = lstContacts.SelectedItem as DLContact;

            if (contact != null)
            {
                RemoveItem(contact, (sender as Control).Tag);

                DisplayContact(contact);
            }
        }

        void addImg_Click(object sender, EventArgs e)
        {
            DLContact contact = lstContacts.SelectedItem as DLContact;

            if (contact != null)
            {
                AddItem(contact, (sender as Control).Tag);

                DisplayContact(contact);
            }
        }

        #endregion

        private void ClearGroup(ImageGroupBox group, int itemCount, int top)
        {
            group.Controls.Clear();
            group.ShowImage = _inEditMode;
            group.Height = 40;
            group.Top = top;
            group.Visible = _inEditMode || (itemCount > 0);
        }

        private void SetNewTop(ImageGroupBox group, ref int top)
        {
            if (group.Visible)
            {
                top += group.Height + 5;
            }
        }

        private void DisplayContact(DLContact contact)
        {
            if (contact == null)
            {
                return;
            }

            this.SuspendLayout();

            try
            {
                int top = lblTitle.Bottom + 2;

                // Remove old org lables
                for (int idx = 0; idx < lblTitle.Parent.Controls.Count; idx++)
                {
                    if (lblTitle.Parent.Controls[idx].Tag is Organization)
                    {
                        lblTitle.Parent.Controls.RemoveAt(idx);
                    }
                }

                foreach (Organization org in contact.Contact.Organizations)
                {
                    Label lblOrg = new Label();
                    lblOrg.Text = !string.IsNullOrEmpty(org.Title) ? string.Format("{0} ({1})", org.Name, org.Title) : org.Name;
                    lblOrg.Top = top;
                    lblOrg.Left = lblTitle.Left;
                    lblOrg.Tag = org;
                    lblOrg.AutoSize = true;
                    lblOrg.Show();
                    top += 15;

                    lblTitle.Parent.Controls.Add(lblOrg);
                }

                top = Math.Max(top, picFace.Bottom + 5);

                lblTitle.Text = contact.ToString();
                lblTitle.Show();

                if (_inEditMode)
                {
                    picEditName.Left = lblTitle.Right + 5;
                    picEditName.Top = lblTitle.Top + ((lblTitle.Height - picEditName.Height) / 2);
                    picEditName.Tag = contact;
                    picEditName.Show();
                }
                else
                {
                    picEditName.Hide();
                }

                if (contact.Picture != null)
                {
                    picFace.Image = contact.Picture;
                }
                else
                {
                    picFace.Image = Resources.small_face;
                }

                picFace.Show();

                ShowItemsInGroup<PhoneNumber>("Mobile", grpPhone, contact.Contact.Phonenumbers, ref top);

                ShowEmailsInGroup<EMail>("Email", grpEmail, contact.Contact.Emails, ref top);

                ShowEmailsInGroup<IMAddress>("Other", grpIMs, contact.Contact.IMs, ref top);

                ShowItemsInGroup<PostalAddress>("Other", grpAddress, contact.Contact.PostalAddresses, ref top);

                grpNotes.Top = top;
                grpNotes.ShowImage = _inEditMode;

                if (contact.HasNotes)
                {
                    // Find the right size for the notes
                    int height;
                    int width;

                    using (Graphics g = lblNotes.CreateGraphics())
                    {
                        SizeF size = g.MeasureString(contact.Notes, lblNotes.Font);
                        height = (int)size.Height;
                        width = (int)size.Width;
                    }

                    grpNotes.ClientSize = new Size(width, height + 50);
                    lblNotes.Height = height;
                    lblNotes.Width = Math.Min(width, grpNotes.Width - lblNotes.Left - 10);
                    lblNotes.Text = contact.Notes;

                    scrollArea_Resize(this, new EventArgs());
                }
                else
                {
                    lblNotes.Text = string.Empty;
                }

                grpNotes.Visible = contact.HasNotes || _inEditMode;
            }
            finally
            {
                this.ResumeLayout();
            }
        }

        private void lstContacts_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (_listBoxDelayTimer.Enabled)
            {
                _listBoxDelayTimer.Stop();
            }

            _listBoxDelayTimer.Start();
        }

        private void edtSearch_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Down)
            {
                lstContacts.SelectedIndex++;
                lstContacts.Focus();
            }
            else if (e.KeyCode == Keys.Up)
            {
                if (lstContacts.SelectedIndex > 0)
                {
                    lstContacts.SelectedIndex--;
                }
                lstContacts.Focus();
            }
        }

        private void mnuLabels_Opening(object sender, CancelEventArgs e)
        {
            // only display the appropriate ones
            if (_selectedLabel != null)
            {
                showMapToolStripMenuItem.Visible = (_selectedLabel.Tag is PostalAddress);
                largeTextToolStripMenuItem.Visible = (_selectedLabel.Tag is PhoneNumber);
                callWithSkypeToolStripMenuItem.Visible = (_selectedLabel.Tag is PhoneNumber);
                sendEmailToolStripMenuItem.Visible = ((_selectedLabel.Tag is EMail) && !(_selectedLabel.Tag is IMAddress));
                copyToolStripMenuItem.Visible = true;

                int c = 0;
                foreach (ToolStripItem item in mnuLabels.Items)
                {
                    if ((item.Visible) && (item.Name != toolStripSeparator.Name))
                    {
                        c++;
                    }
                }

                toolStripSeparator.Visible = (c != 1);
            }
        }

        private void sendEmailToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if ((_selectedLabel != null) && (_selectedLabel.Tag is EMail))
            {
                EMail email = (_selectedLabel.Tag as EMail);
                if (!string.IsNullOrEmpty(email.Address))
                {
                    try
                    {
                        Process.Start(string.Format("mailto:{0}", email.Address));
                    }
                    catch (Win32Exception)
                    {
                        ShowMessage("Unable to find default email application", "Error Sending Email", MessageBoxIcon.Error, MessageBoxButtons.OK);
                    }
                }
            }
        }

        private void showMapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if ((_selectedLabel != null) && (_selectedLabel.Tag is PostalAddress))
            {
                string address = (_selectedLabel.Tag as PostalAddress).Value;

                if (!string.IsNullOrEmpty(address))
                {
                    address = address.Replace(Environment.NewLine, "+");
                    address = address.Replace('\n', '+');
                    address = address.Replace('\r', '+');
                    address = address.Replace(' ', '+');
                    address = address.Replace("+++", "+");
                    address = address.Replace("++", "+");
                    try
                    {
                        Process.Start(string.Format("http://maps.google.com/maps?q={0}", address));
                    }
                    catch
                    {
                        // Do nothing
                    }
                }
            }
        }

        private bool ParsePhoneForSkype(ref string inputPhone)
        {
            bool hasIssues = false;

            hasIssues = inputPhone.IndexOf("(0") != -1;

            hasIssues |= inputPhone.ToLower().IndexOf("ext") != -1;

            if (hasIssues)
            {
                inputPhone = inputPhone.Replace("(0", string.Empty);
                inputPhone = inputPhone.Replace(")", string.Empty);
                inputPhone = inputPhone.Replace("(", string.Empty);

                int p = inputPhone.ToLower().IndexOf("ext");
                if (p != -1)
                {
                    inputPhone = inputPhone.Remove(p);
                }

                inputPhone = inputPhone.Replace(".", string.Empty);
                inputPhone = inputPhone.Replace(":", string.Empty);
                inputPhone = inputPhone.Replace("-", string.Empty);
            }

            if (!inputPhone.StartsWith("+") && !(inputPhone.StartsWith("00")))
            {
                inputPhone = string.Format("+{0}", inputPhone);
                hasIssues = true;
            }

            return hasIssues;

        }

        public void DialWithSkype(string phoneNumber)
        {
            string originalNumber = phoneNumber;

            if (!string.IsNullOrEmpty(phoneNumber))
            {
                try
                {
                    if (ParsePhoneForSkype(ref phoneNumber))
                    {
                        switch (frmCleanUpPhone.Show(phoneNumber))
                        {
                            case DialogResult.No:
                                phoneNumber = originalNumber;
                                break;
                            case DialogResult.Cancel:
                                return;
                        }
                    }

                    Process.Start(string.Format("skype:{0}", phoneNumber));
                }
                catch (Win32Exception)
                {
                    ShowMessage("Unable to connect to Skype. Please make sure it is installed", "Skype Error", MessageBoxIcon.Error, MessageBoxButtons.OK);
                }
            }
        }

        private void callWithSkypeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if ((_selectedLabel != null) && (_selectedLabel.Tag is PhoneNumber))
            {
                string phoneNumber = (_selectedLabel.Tag as PhoneNumber).Value;

                DialWithSkype(phoneNumber);
            }
        }

        private void StoreWindowLocation()
        {
            Settings.Default.WindowL = this.Left;
            Settings.Default.WindowT = this.Top;
            Settings.Default.ContactsPanelSize = lstContacts.Width;
            Settings.Default.WindowW = this.Width;
            Settings.Default.WindowH = this.Height;

            Settings.Default.Save();
        }

        private void frmMain_Resize(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                trayIcon.Visible = true;
                _runMinimised = true;
                this.Hide();
            }
            else if (WindowState == FormWindowState.Normal)
            {
                trayIcon.Visible = false;
                //edtSearch.Focus();
            }
        }

        private void Minimise()
        {
            this.Hide();
            WindowState = FormWindowState.Minimized;
            trayIcon.Visible = true;
        }

        private void BringBack()
        {
            _runMinimised = false;
            this.Show();
            this.WindowState = FormWindowState.Normal;
            trayIcon.Visible = false;
        }

        internal void ShowError(string message, string title)
        {
            ShowMessage(message, title, MessageBoxIcon.Error, MessageBoxButtons.OK);
        }

        internal void ShowMessage(string message, string title, MessageBoxIcon icon, MessageBoxButtons buttons)
        {
            if (!_runMinimised)
            {
                MessageBox.Show(message, title, buttons, icon);
            }
            else
            {
                ToolTipIcon trayIconImage = ToolTipIcon.Info;

                switch (icon)
                {
                    case MessageBoxIcon.Asterisk:
                        trayIconImage = ToolTipIcon.Info;
                        break;
                    case MessageBoxIcon.Error:
                        trayIconImage = ToolTipIcon.Error;
                        break;
                    case MessageBoxIcon.None:
                        trayIconImage = ToolTipIcon.None;
                        break;
                    case MessageBoxIcon.Question:
                        trayIconImage = ToolTipIcon.Info;
                        break;
                    case MessageBoxIcon.Warning:
                        trayIconImage = ToolTipIcon.Warning;
                        break;
                    default:
                        trayIconImage = ToolTipIcon.None;
                        break;
                }

                trayIcon.ShowBalloonTip(5000, title, message, trayIconImage);
            }
        }

        private void trayIcon_BalloonTipClicked(object sender, EventArgs e)
        {
            BringBack();
        }

        private void trayIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            BringBack();
        }

        private void imgClear_Click(object sender, EventArgs e)
        {
            edtSearch.Text = string.Empty;
            ShowContactList();
            edtSearch.Focus();
        }

        private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                if (this.WindowState == FormWindowState.Normal)
                {
                    StoreWindowLocation();
                }

                if (lstContacts.SelectedItem != null)
                {
                    Settings.Default.LastContact = (lstContacts.SelectedItem as DLContact).Contact.SelfUri.Content;
                }

                Settings.Default.RunMinimised = _runMinimised;

                Settings.Default.Save();
            }
            catch
            {
                // Don't do anything. It is closing
            }
        }

        private void frmMain_VisibleChanged(object sender, EventArgs e)
        {
            if ((_runMinimised) && (WindowState != FormWindowState.Minimized))
            {
                Visible = false;
                Minimise();
            }
        }

        private void largeTextToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if ((_selectedLabel != null) && (_selectedLabel.Tag is PhoneNumber))
            {
                PhoneNumber phone = _selectedLabel.Tag as PhoneNumber;

                frmLargeText.ShowLargeText(phone.Value);
            }
        }

        private void btnSettings_Click(object sender, EventArgs e)
        {
            frmSettings frm = new frmSettings(this);
            if (frm.ShowDialog() == DialogResult.OK)
            {
                LoadAllContacts(true);
                _minimiseOnClose = (MinimiseOnCloseOptions)Enum.Parse(typeof(MinimiseOnCloseOptions), Settings.Default.MinimiseOnClose);
                _nameOrder = (NameDisplayType)Settings.Default.NameOrder;
            }
        }

        private void scrollArea_Resize(object sender, EventArgs e)
        {
            grpNotes.Width = scrollArea.ClientSize.Width - 11;
            btnEdit.Left = scrollArea.Left;
            btnCancel.Left = btnEdit.Right + 5;

            btnAdd.Left = scrollArea.Left - 30;
            btnRemove.Left = btnAdd.Left - 26;
        }

        private void scrollArea_MouseClick(object sender, MouseEventArgs e)
        {
            (sender as Control).Focus();
        }

        private void edtSearch_Enter(object sender, EventArgs e)
        {
            edtSearch.Text = string.Empty;
            ShowContactList();
            edtSearch.Focus();
        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_forceClose)
            {
                e.Cancel = false;
                return;
            }

            if (e.CloseReason == CloseReason.UserClosing)
            {
                if (_minimiseOnClose == MinimiseOnCloseOptions.Ask)
                {
                    DialogResult result = frmAskMinimise.Show();

                    if (result == DialogResult.Yes)
                    {
                        // minimise
                        e.Cancel = true;
                        Minimise();
                    }
                    else if (result == DialogResult.No)
                    {
                        // close
                        e.Cancel = false;
                    }
                    else
                    {
                        // do nothing
                        e.Cancel = true;
                    }

                    _minimiseOnClose = (MinimiseOnCloseOptions)Enum.Parse(typeof(MinimiseOnCloseOptions), Settings.Default.MinimiseOnClose);
                }
                else
                {
                    if (_minimiseOnClose == MinimiseOnCloseOptions.No)
                    {
                        e.Cancel = false;
                    }
                    else
                    {
                        e.Cancel = true;
                        Minimise();
                    }
                }
            }
            else
            {
                e.Cancel = false;
            }
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            trayIcon_BalloonTipClicked(sender, e);
        }

        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _forceClose = true;

            bool wasMinimised = (WindowState == FormWindowState.Minimized);

            BringBack();
            trayIcon.Dispose();

            _runMinimised = wasMinimised;
            Close();
        }

        private void lstContacts_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (e.Index != -1)
            {
                DLContact contact = ((ListBox)sender).Items[e.Index] as DLContact;

                e.DrawBackground();

                Brush myBrush = Brushes.Black;
                if (((e.State & DrawItemState.Focus) == DrawItemState.Focus) || 
                   ((e.State & DrawItemState.Selected) == DrawItemState.Selected))
                {
                    myBrush = Brushes.White;
                }

                if ((!contact.HasTitle) && (!Settings.Default.SameEmailOnly))
                {
                    myBrush = Brushes.DarkGray;
                }

                e.Graphics.DrawString(contact.ToString(),e.Font, myBrush, e.Bounds, StringFormat.GenericDefault);

                e.DrawFocusRectangle();
            }
        }

        private string GetTextualValue(object obj)
        {
            if (obj is PhoneNumber)
            {
                return (obj as PhoneNumber).Value;
            }
            else if (obj is IMAddress)
            {
                return (obj as IMAddress).Address;
            }
            else if (obj is PostalAddress)
            {
                return (obj as PostalAddress).Value;
            }
            else if (obj is EMail)
            {
                return (obj as EMail).Address;
            }
            else
            {
                return string.Empty;
            }
        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_selectedLabel != null)
            {
                string value = GetTextualValue(_selectedLabel.Tag);

                if (!string.IsNullOrEmpty(value))
                {
                    Clipboard.SetText(value);
                }
            }
        }

        private void AddItem(object item)
        {
            DLContact contact = lstContacts.SelectedItem as DLContact;

            if (contact != null)
            {
                AddItem(contact, item);

                DisplayContact(contact);
            }
        }

        private void grpEmail_ImageClick(object sender, EventArgs e)
        {
            AddItem(new EMail());
        }

        private void grpPhone_ImageClick(object sender, EventArgs e)
        {
            AddItem(new PhoneNumber());
        }

        private void grpIMs_ImageClick(object sender, EventArgs e)
        {
            AddItem(new IMAddress());
        }

        private void grpAddress_ImageClick(object sender, EventArgs e)
        {
            AddItem(new PostalAddress());
        }

        void title_ShouldExit(object sender, EventArgs e)
        {
            TextBox editor = sender as TextBox;

            if (!string.IsNullOrEmpty(editor.Text.Trim()))
            {
                Label label = editor.Tag as Label;
                DLContact contact = lstContacts.SelectedItem as DLContact;

                if (contact != null)
                {
                    contact.Contact.Title.Text = editor.Text;
                    label.Text = editor.Text;
                    ShowContactList();
                }
            }
        }

        private void grpNotes_ImageClick(object sender, EventArgs e)
        {
            DLContact contact = lstContacts.SelectedItem as DLContact;

            if (contact != null)
            {
                EditNotes(contact);

                DisplayContact(contact);
            }
        }

        private void DeleteContact()
        {
            if (MessageBox.Show("Are you sure?", "Please confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                DLContact contact = lstContacts.SelectedItem as DLContact;

                try
                {
                    lstContacts.BeginUpdate();
                    try
                    {
                        _allcontacts.Remove(contact);
                        _displayedContacts.Remove(contact);

                        lstContacts.DataSource = null;
                        lstContacts.DataSource = _displayedContacts;

                        contact.Contact.Delete();
                    }
                    finally
                    {
                        lstContacts.EndUpdate();
                    }
                }
                catch (Exception exc)
                {
                    ShowMessage(exc.Message, "Error", MessageBoxIcon.Error, MessageBoxButtons.OK);
                    LoadAllContacts(true);
                }
            }
        }

        private void lstContacts_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                DeleteContact();
            }
            else if (e.KeyCode == Keys.Insert)
            {
                AddNewContact();
            }
        }

        private void AddNewContact()
        {
            try
            {
                ContactEntry entry = new ContactEntry();
                entry.Title.Text = "New Contact";
                DLContact contact = new DLContact(entry, (NameDisplayType)Settings.Default.NameOrder, true);

                lstContacts.BeginUpdate();
                try
                {
                    _allcontacts.Add(contact);
                    _displayedContacts.Add(contact);

                    lstContacts.DataSource = null;
                    lstContacts.DataSource = _displayedContacts;
                }
                finally
                {
                    lstContacts.EndUpdate();
                }

                // Show it
                lstContacts.SelectedItem = contact;
                EnterEditMode();
            }
            catch(Exception exc)
            {
                ShowMessage(exc.Message, "Error", MessageBoxIcon.Error, MessageBoxButtons.OK);
                LoadAllContacts(true);
            }
        }

        private void picEditName_Click(object sender, EventArgs e)
        {
            DLContact contact = (sender as Control).Tag as DLContact;

            using (frmEditName frm = new frmEditName())
            {
                List<string> orgs = new List<string>();
                foreach(Organization org in contact.Contact.Organizations)
                {
                    if (org.Title != null)
                    {
                        orgs.Add(string.Format("{0} ({1})", org.Name, org.Title));
                    }
                    else
                    {
                        orgs.Add(org.Name);
                    }
                }

                if (frm.Show(contact.Contact.Title.Text, orgs) == DialogResult.OK)
                {
                    contact.ContactChanged = true;
                    contact.Contact.Title.Text = frm.ContactName;

                    contact.Contact.Organizations.Clear();
                    foreach (string orgInfo in frm.Organisations)
                    {
                        Organization org = new Organization();

                        // Parse it Name (title)
                        int p = orgInfo.IndexOf('(');

                        if (p != -1)
                        {
                            string orgName = orgInfo.Substring(0, p - 1);
                            int k = orgInfo.IndexOf(')');
                            string orgTitle;

                            if ((k != -1) && (k > p))
                            {
                                orgTitle = orgInfo.Substring(p + 1, k - p - 1);
                            }
                            else
                            {
                                orgTitle = orgInfo.Substring(p + 1);
                            }

                            org.Name = orgName;
                            org.Title = orgTitle;
                        }
                        else
                        {
                            org.Name = orgInfo;
                        }

                        // Set the rel.
                        // TODO: This should be modifiable
                        org.Rel = "http://schemas.google.com/g/2005#work";

                        contact.Contact.Organizations.Add(org);
                    }

                    DisplayContact(contact);
                }
            }
        }

        private void btnEdit_Click(object sender, EventArgs e)
        {
            if (!_inEditMode)
            {
                EnterEditMode();
            }
            else
            {
                // Save it and...
                ExitEditMode(true);
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            ExitEditMode(false);
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            AddNewContact();
        }

        private void btnRemove_Click(object sender, EventArgs e)
        {
            DeleteContact();
        }

        private void picFace_Click(object sender, EventArgs e)
        {
            if (_inEditMode)
            {
                DLContact contact = lstContacts.SelectedItem as DLContact;

                if (contact != null)
                {
                    if (contact.NewContact)
                    {
                        ShowMessage("Currently it is not possible to add picture to a new contact. You need to save the contact and add the picture later. This will be fixed in future releases", "Error", MessageBoxIcon.Error, MessageBoxButtons.OK);
                        return;
                    }
                    else
                    {
                        using (frmEditPicture frm = new frmEditPicture())
                        {
                            if (frm.Show(contact) == DialogResult.OK)
                            {
                                if (contact.Picture != null)
                                {
                                    picFace.Image = contact.Picture;
                                }
                                else
                                {
                                    picFace.Image = Resources.small_face;
                                    LoadAllContacts(false);
                                }
                            }
                        }
                    }
                }
            }
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            LoadAllContacts(true);
        }
    }
}